package merisis.traitspace;

import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.solr.util.OpenBitSet;

import merisis.MerisisVM;
import merisis.contentcloud.ContentCloud;
import merisis.util.BitUtil;

import com.google.inject.Inject;
import com.google.inject.Injector;

public class EntityHappen {

	@Inject
	private Logger logger;

	@Inject
	private MerisisVM mvm;

	@Inject
	private ContentCloud cloud;

	@Inject
	private Injector injector;

	public void happen(long[] ontos, Object object) {
		
		happen(ontos, object, -1);
	}

	public void happen(long[] ontos, Object object, long place) {

		mvm.give(ontos);
		mvm.source();
		mvm.focus();
		long[] onto = mvm.read();

		long occ1 = -1;
		long occ2 = -1;
		long objc;
		if (onto == null || onto.length == 0) {

			occ1 = mvm.occur(ontos);
		} else if (onto.length == 1) {

			occ1 = onto[0];
		} else {
			throw new AmbiguityException(ontos);
		}

		if (place == -1) {
			objc = cloud.occur(object);
		} else {

			mvm.give(place);
			mvm.stepin();
			try {
				objc = cloud.occur(object);
			} finally {
				mvm.stepout();
			}
		}

		occ2 = mvm.occur(occ1, objc);

		if (logger.isLoggable(Level.FINEST)) {

			StringBuilder sb = new StringBuilder();
			sb.append("((ontos {");
			BitUtil.format(sb, ontos, ",");
			sb.append("}): " + occ1);
			sb.append(", object:" + objc);
			sb.append("):" + occ2);
			logger.finest(sb.toString());
		}

		ripple(occ2);
	}

	public Object retrieve(long[] ontos) {

		long[] onto;
		mvm.give(ontos);
		mvm.stepin();
		try {
			mvm.focus();
			onto = mvm.read();
		} finally {
			mvm.stepout();
		}

		if (logger.isLoggable(Level.FINEST)) {
			logger.finest("ontos focus: " + BitUtil.toString(onto));
		}

		mvm.autobeam();
		try {
			// NOTICE: changes the souces instead of the focus.
			mvm.give(ontos);
			mvm.tprism();
			mvm.complement();
			onto = mvm.read();
		} finally {
			mvm.stepout();
		}

		if (onto == null || onto.length == 0)
			return null;

		if (onto.length > 1)
			throw new AmbiguityException(null);

		return cloud.retrieve(onto[0]);
	}

	private HashMap<OpenBitSet, Class<? extends HappenReaction>> reactionMap = new HashMap<OpenBitSet, Class<? extends HappenReaction>>();

	public void ripple(long center) {

		for (OpenBitSet k : reactionMap.keySet()) {

			mvm.give(BitUtil.toArray(k));
			mvm.source();
			mvm.give(center);
			mvm.focus();
			long[] test = mvm.read();

			if (test == null)
				continue;

			if (logger.isLoggable(Level.FINEST)) {
				StringBuilder sb = new StringBuilder();
				sb.append("rippling, center: " + center);
				sb.append(", reacted-ontos: {");
				BitUtil.format(sb, k, ",");
				sb.append("}");
				logger.finest(sb.toString());
			}

			Class<? extends HappenReaction> cls;
			cls = reactionMap.get(k);
			try {
				HappenReaction r = cls.newInstance();
				injector.injectMembers(r);

				//
				mvm.give(center);
				mvm.source();
				mvm.give(center);
				mvm.focus();
				mvm.autobeam();
				try {
					mvm.give(BitUtil.toArray(k));
					mvm.tprism();
					mvm.complement();
				} finally {
					mvm.stepout();
				}
				long[] pts = mvm.read();
				if (pts == null) {
					continue;
				}

				r.happen(pts);
			} catch (Exception e) {
				logger.log(Level.WARNING, e.getMessage(), e);
			}
		}
	}

	public void ripple(long[] ontos, Class<? extends HappenReaction> reactor) {

		OpenBitSet bs = BitUtil.bitsetOrArray(new OpenBitSet(), ontos);
		reactionMap.put(bs, reactor);
	}
}
